#include "jsonOperation.h"
#include <gmock/gmock.h>
#include "soapStub.h"
#include <json/json.hpp>
#include <iostream>
using namespace std;
using nlohmann::json;
using namespace testing;
using namespace onvif_tool_json_operation;

class jsonOperationTest : public Test
{
public:
    void SetUp() override
    {
    }
};

TEST_F(jsonOperationTest, parseConfig)
{
    string jsonResponse;
    tt__Config config;

    config.Name = (char *)"name";
    config.Type = (char *)"type";

    tt__ItemList itemlist;
    _tt__ItemList_SimpleItem simpleItem[2];
    simpleItem[0].Name = (char *)"name0";
    simpleItem[0].Value = (char *)"value0";
    simpleItem[0].Name = (char *)"name1";
    simpleItem[0].Value = (char *)"value1";
    itemlist.SimpleItem = simpleItem;

    _tt__ItemList_ElementItem elementItem[2];
    elementItem[0].Name = (char *)"name0";
    elementItem[1].Name = (char *)"name1";
    itemlist.ElementItem = elementItem;

    itemlist.__sizeSimpleItem = 2;
    itemlist.__sizeElementItem = 2;
    config.Parameters = &itemlist;

    auto err = jsonOperation::parseConfig(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);
    cout << "Config: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parse1DSpace)
{
    string jsonResponse;
    tt__Space1DDescription space;
    space.URI = (char *)"uriuriuriuri";
    space.XRange = new tt__FloatRange();
    space.XRange->Min = 0.00;
    space.XRange->Max = 1.25;
    auto err = jsonOperation::parse1DSpace(jsonResponse, &space);
    ASSERT_THAT(err, nullopt);
    cout << "1Dspace: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parse2DSpace)
{
    string jsonResponse;
    tt__Space2DDescription space;
    space.URI = (char *)"uriuriuriuri";
    space.XRange = new tt__FloatRange();
    space.YRange = new tt__FloatRange();
    space.XRange->Min = 0.00;
    space.XRange->Max = 1.25;
    space.YRange->Min = -9.0;
    space.YRange->Max = 9.0;
    auto err = jsonOperation::parse2DSpace(jsonResponse, &space);
    ASSERT_THAT(err, nullopt);
    cout << "2Dspace: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parsePTZSpace)
{
    string jsonResponse;
    tt__PTZSpaces space;
    space.__sizeAbsolutePanTiltPositionSpace = 2;
    space.AbsolutePanTiltPositionSpace = new tt__Space2DDescription *[2];
    space.AbsolutePanTiltPositionSpace[0] = new tt__Space2DDescription();
    space.AbsolutePanTiltPositionSpace[1] = new tt__Space2DDescription();
    space.AbsolutePanTiltPositionSpace[0]->URI = (char *)"uriuriuriuri";
    space.AbsolutePanTiltPositionSpace[0]->XRange = new tt__FloatRange();
    space.AbsolutePanTiltPositionSpace[0]->XRange->Min = 0.00;
    space.AbsolutePanTiltPositionSpace[0]->XRange->Max = 1.25;
    space.AbsolutePanTiltPositionSpace[0]->YRange = new tt__FloatRange();
    space.AbsolutePanTiltPositionSpace[0]->YRange->Min = 0.00;
    space.AbsolutePanTiltPositionSpace[0]->YRange->Max = 1.25;

    space.AbsolutePanTiltPositionSpace[1]->URI = (char *)"uri2uri2uri2uri2";
    space.AbsolutePanTiltPositionSpace[1]->XRange = new tt__FloatRange();
    space.AbsolutePanTiltPositionSpace[1]->XRange->Min = 2.00;
    space.AbsolutePanTiltPositionSpace[1]->XRange->Max = 2.25;
    space.AbsolutePanTiltPositionSpace[1]->YRange = new tt__FloatRange();
    space.AbsolutePanTiltPositionSpace[1]->YRange->Min = 4.00;
    space.AbsolutePanTiltPositionSpace[1]->YRange->Max = 4.25;
    auto err = jsonOperation::parsePTZSpace(jsonResponse, &space);
    ASSERT_THAT(err, nullopt);
    cout << "PTZspace: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parsePTZVector)
{
    string jsonResponse;
    tt__PTZVector PTZVector;

    tt__Vector1D zoom;
    zoom.x = 1.0;
    zoom.space = (char *)"space";

    PTZVector.Zoom = &zoom;

    tt__Vector2D panTilt;
    panTilt.x = 2.0;
    panTilt.y = 3.0;
    panTilt.space = (char *)"space";
    PTZVector.PanTilt = &panTilt;

    auto err = jsonOperation::parsePTZVector(jsonResponse, &PTZVector);
    ASSERT_THAT(err, nullopt);
    cout << "parsePTZVector: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseGeoLocation)
{
    string jsonResponse;
    double lat = 1.0, lon = 2.0;
    float elevation = 3.0;

    tt__GeoLocation location;
    location.lat = &lat;
    location.lon = &lon;
    location.elevation = &elevation;

    auto err = jsonOperation::parseGeoLocation(jsonResponse, &location);
    ASSERT_THAT(err, nullopt);

    cout << "GeoLocation: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseGeoOrientation)
{
    string jsonResponse;
    float roll = 3.0, pitch = 1.1, yaw = 2.3;

    tt__GeoOrientation orientation;
    orientation.roll = &roll;
    orientation.pitch = &pitch;
    orientation.yaw = &yaw;

    auto err = jsonOperation::parseGeoOrientation(jsonResponse, &orientation);
    ASSERT_THAT(err, nullopt);

    cout << "GeoOrientation: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseLocalLocation)
{
    string jsonResponse;
    float x = 3.0, y = 1.1, z = 2.3;

    tt__LocalLocation location;
    location.x = &x;
    location.y = &y;
    location.z = &z;

    auto err = jsonOperation::parseLocalLocation(jsonResponse, &location);
    ASSERT_THAT(err, nullopt);

    cout << "LocalLocation: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseLocalOrientation)
{
    string jsonResponse;
    float roll = 3.0, pan = 1.1, tilt = 2.3;

    tt__LocalOrientation orientation;
    orientation.roll = &roll;
    orientation.pan = &pan;
    orientation.tilt = &tilt;

    auto err = jsonOperation::parseLocalOrientation(jsonResponse, &orientation);
    ASSERT_THAT(err, nullopt);

    cout << "LocalOrientation: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseLocationEntity)
{
    string jsonResponse;
    float roll = 3.0, pan = 1.1, tilt = 2.3;

    tt__LocalOrientation orientation;
    orientation.roll = &roll;
    orientation.pan = &pan;
    orientation.tilt = &tilt;

    tt__LocationEntity entity;
    entity.Token = (char *)"eohcsidln";
    entity.LocalOrientation = &orientation;

    auto err = jsonOperation::parseLocationEntity(jsonResponse, &entity);
    ASSERT_THAT(err, nullopt);

    cout << "LocationEntity: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseNetworkInterface)
{
    string jsonResponse;
    tt__NetworkInterface interface_;
    interface_.Enabled = true;

    tt__NetworkInterfaceInfo info;
    int mtu = 22;
    info.Name = (char *)"iName";
    info.HwAddress = (char *)"macAddr";
    info.MTU = &mtu;
    interface_.Info = &info;

    tt__IPv4NetworkInterface ipv4;
    tt__IPv4Configuration config;
    config.__sizeManual = 0;

    tt__PrefixedIPv4Address fromDHCP;
    fromDHCP.Address = (char *)"122.12.12.12";
    fromDHCP.PrefixLength = 30;

    config.FromDHCP = &fromDHCP;
    config.DHCP = true;

    ipv4.Enabled = true;
    ipv4.Config = &config;
    interface_.IPv4 = &ipv4;

    auto err = jsonOperation::parseNetworkInterface(jsonResponse, &interface_);
    ASSERT_THAT(err, nullopt);

    cout << "NetworkInterface: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoSource_Using_Imaging_V1)
{
    string jsonResponse;
    tt__VideoSource videoSource;

    videoSource.token = (char *)"token";
    videoSource.Framerate = 60.0;

    tt__VideoResolution resolution;
    resolution.Width = 220;
    resolution.Height = 111;
    videoSource.Resolution = &resolution;

    tt__ImagingSettings imaging;

    tt__BacklightCompensation backlightCompensation;
    backlightCompensation.Mode = (tt__BacklightCompensationMode)1;
    backlightCompensation.Level = 1.0;
    imaging.BacklightCompensation = &backlightCompensation;

    float val1 = 2.3;
    imaging.Brightness = &val1;
    imaging.ColorSaturation = &val1;
    imaging.Contrast = &val1;

    tt__Exposure exposure;
    exposure.Mode = (tt__ExposureMode)1;
    exposure.Priority = (tt__ExposurePriority)1;

    tt__Rectangle window;
    window.bottom = &val1;
    window.left = &val1;
    window.right = &val1;
    window.top = &val1;
    exposure.Window = &window;

    exposure.MinExposureTime = 292.1;
    exposure.MaxExposureTime = 592.1;
    exposure.MinGain = 292.1;
    exposure.MaxGain = 592.1;
    exposure.MinIris = 292.1;
    exposure.MaxIris = 592.1;
    exposure.ExposureTime = 292.1;
    exposure.Gain = 592.1;
    exposure.Iris = 292.1;
    imaging.Exposure = &exposure;

    tt__FocusConfiguration focus;
    focus.AutoFocusMode = (tt__AutoFocusMode)1;
    focus.DefaultSpeed = 8.3;
    focus.NearLimit = 12.3;
    focus.FarLimit = 489;
    imaging.Focus = &focus;

    tt__IrCutFilterMode ircut = (tt__IrCutFilterMode)2;
    imaging.IrCutFilter = &ircut;
    imaging.Sharpness = &val1;

    tt__WideDynamicRange wideDyn;
    wideDyn.Mode = (tt__WideDynamicMode)1;
    wideDyn.Level = 234;
    imaging.WideDynamicRange = &wideDyn;

    tt__WhiteBalance whiteBalance;
    whiteBalance.Mode = (tt__WhiteBalanceMode)0;
    whiteBalance.CrGain = 2342;
    whiteBalance.CbGain = 324;
    imaging.WhiteBalance = &whiteBalance;
    imaging.Extension = nullptr;

    videoSource.Imaging = &imaging;
    videoSource.Extension = nullptr;

    auto err = jsonOperation::parseVideoSource(jsonResponse, &videoSource);
    ASSERT_THAT(err, nullopt);

    cout << "VideoSource using imaging_v1: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoSource_Using_Imaging_V2)
{
    string jsonResponse;
    tt__VideoSource videoSource;

    videoSource.token = (char *)"token";
    videoSource.Framerate = 60.0;

    tt__VideoResolution resolution;
    resolution.Width = 220;
    resolution.Height = 111;
    videoSource.Resolution = &resolution;

    // 设置Imaging v1 和Imaging v2 同时存在
    tt__ImagingSettings imaging_v1;

    tt__BacklightCompensation backlightCompensation_v1;
    backlightCompensation_v1.Mode = (tt__BacklightCompensationMode)1;
    backlightCompensation_v1.Level = 1.0;
    imaging_v1.BacklightCompensation = &backlightCompensation_v1;

    float val_v1 = 4645.7;
    imaging_v1.Brightness = &val_v1;
    imaging_v1.ColorSaturation = &val_v1;
    imaging_v1.Contrast = &val_v1;

    videoSource.Imaging = &imaging_v1;

    tt__VideoSourceExtension extension;

    // Imaging v2
    tt__ImagingSettings20 imaging;
    float val1 = 2.3;

    tt__BacklightCompensation20 backlightCompensation;
    backlightCompensation.Mode = (tt__BacklightCompensationMode)1;
    backlightCompensation.Level = &val1;
    imaging.BacklightCompensation = &backlightCompensation;

    imaging.Brightness = &val1;
    imaging.ColorSaturation = &val1;
    imaging.Contrast = &val1;

    tt__Exposure20 exposure;
    exposure.Mode = (tt__ExposureMode)1;
    tt__ExposurePriority priority = (tt__ExposurePriority)1;
    exposure.Priority = &priority;

    tt__Rectangle window;
    window.bottom = &val1;
    window.left = &val1;
    window.right = &val1;
    window.top = &val1;
    exposure.Window = &window;

    exposure.MinExposureTime = &val1;
    exposure.MaxExposureTime = &val1;
    exposure.MinGain = &val1;
    exposure.MaxGain = &val1;
    exposure.MinIris = &val1;
    exposure.MaxIris = &val1;
    exposure.ExposureTime = &val1;
    exposure.Gain = &val1;
    exposure.Iris = &val1;
    imaging.Exposure = &exposure;

    tt__FocusConfiguration20 focus;
    focus.AFMode = (char *)"AFMode";
    focus.AutoFocusMode = (tt__AutoFocusMode)1;
    focus.DefaultSpeed = &val1;
    focus.NearLimit = &val1;
    focus.FarLimit = &val1;
    imaging.Focus = &focus;

    tt__IrCutFilterMode ircut = (tt__IrCutFilterMode)2;
    imaging.IrCutFilter = &ircut;
    imaging.Sharpness = &val1;

    tt__WideDynamicRange20 wideDyn;
    wideDyn.Mode = (tt__WideDynamicMode)1;
    wideDyn.Level = &val1;
    imaging.WideDynamicRange = &wideDyn;

    tt__WhiteBalance20 whiteBalance;
    whiteBalance.Mode = (tt__WhiteBalanceMode)0;
    whiteBalance.CrGain = &val1;
    whiteBalance.CbGain = &val1;
    imaging.WhiteBalance = &whiteBalance;

    tt__ImagingSettingsExtension20 extension2;
    tt__ImageStabilization imageStabilization;
    imageStabilization.Mode = (tt__ImageStabilizationMode)0;
    imageStabilization.Level = &val1;
    imageStabilization.Extension = nullptr;
    extension2.ImageStabilization = &imageStabilization;

    tt__ImagingSettingsExtension202 extension3;
    tt__IrCutFilterAutoAdjustment *irCut[2];
    irCut[0] = new tt__IrCutFilterAutoAdjustment;
    irCut[1] = new tt__IrCutFilterAutoAdjustment;

    irCut[0]->BoundaryType = (char *)"BoundaryType0";
    irCut[1]->BoundaryType = (char *)"BoundaryType1";
    irCut[0]->BoundaryOffset = &val1;
    irCut[1]->BoundaryOffset = &val1;
    irCut[0]->ResponseTime = (char *)"ResponseTime0";
    irCut[1]->ResponseTime = (char *)"ResponseTime1";
    irCut[0]->Extension = nullptr;
    irCut[1]->Extension = nullptr;

    extension3.__sizeIrCutFilterAutoAdjustment = 2;
    extension3.IrCutFilterAutoAdjustment = irCut;

    tt__ImagingSettingsExtension203 extension4;
    tt__ToneCompensation tone;
    tone.Mode = (char *)"tone.Mode";
    tone.Level = &val1;
    extension4.ToneCompensation = &tone;
    extension4.Defogging = nullptr;
    extension4.NoiseReduction = nullptr;
    extension4.Extension = nullptr;

    extension3.Extension = &extension4;

    extension2.Extension = &extension3;

    imaging.Extension = &extension2;

    extension.Imaging = &imaging;
    extension.Extension = nullptr;
    videoSource.Extension = &extension;

    auto err = jsonOperation::parseVideoSource(jsonResponse, &videoSource);
    ASSERT_THAT(err, nullopt);

    cout << "VideoSource using imaging_v2: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoSourceConfiguration)
{
    string jsonResponse;
    tt__VideoSourceConfiguration config;
    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.ViewMode = (char *)"viewMode";
    config.SourceToken = (char *)"sourceToken";

    // bound
    tt__IntRectangle bounds;
    bounds.x = 1;
    bounds.y = 2;
    bounds.width = 3;
    bounds.height = 4;

    config.Bounds = &bounds;

    // extension
    tt__VideoSourceConfigurationExtension extension;

    // rotate
    tt__Rotate rotate;
    rotate.Mode = (tt__RotateMode)2;
    int degree = 1;
    rotate.Degree = &degree;
    rotate.Extension = nullptr;

    extension.Rotate = &rotate;

    // extension2
    tt__VideoSourceConfigurationExtension2 extension2;
    extension2.__sizeLensDescription = 2;

    tt__LensDescription *LensDescriptions[2];
    LensDescriptions[0] = new tt__LensDescription;
    LensDescriptions[1] = new tt__LensDescription;

    float focalLenth = 1.0;
    float focalLenth2 = 2.0;
    LensDescriptions[0]->FocalLength = &focalLenth;
    LensDescriptions[1]->FocalLength = &focalLenth2;

    float val = 1.1;
    float val2 = 2.2;
    tt__LensOffset offset;
    offset.x = &val;
    offset.y = &val;
    LensDescriptions[0]->Offset = &offset;
    LensDescriptions[1]->Offset = &offset;

    LensDescriptions[0]->__sizeProjection = 0;
    LensDescriptions[0]->Projection = nullptr;

    tt__LensProjection *Projections[2];
    Projections[0] = new tt__LensProjection;
    Projections[1] = new tt__LensProjection;
    Projections[0]->Radius = 0.1;
    Projections[1]->Radius = 0.2;
    Projections[0]->Angle = 11.1;
    Projections[1]->Angle = 12.2;
    Projections[0]->Transmittance = nullptr;
    Projections[1]->Transmittance = &val2;

    LensDescriptions[1]->__sizeProjection = 2;
    LensDescriptions[1]->Projection = Projections;
    LensDescriptions[0]->XFactor = 111;
    LensDescriptions[1]->XFactor = 222;

    extension2.LensDescription = LensDescriptions;

    tt__SceneOrientation SceneOrientation;
    SceneOrientation.Mode = (tt__SceneOrientationMode)0;
    SceneOrientation.Orientation = (char *)"orientation";
    extension2.SceneOrientation = &SceneOrientation;

    extension.Extension = &extension2;

    config.Extension = &extension;

    auto err = jsonOperation::parseVideoSourceConfiguration(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "VideoSourceConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoOutputConfiguration)
{
    string jsonResponse;
    tt__VideoOutputConfiguration config;
    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.OutputToken = (char *)"outputToken";

    auto err = jsonOperation::parseVideoOutputConfiguration(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "VideoOutputConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoSourceConfigurationOptions)
{
    string jsonResponse;
    tt__VideoSourceConfigurationOptions option;
    int val1 = 3;
    option.MaximumNumberOfProfiles = &val1;

    tt__IntRectangleRange bound;
    tt__IntRange range;
    range.Min = -1;
    range.Max = 2;
    bound.XRange = &range;
    bound.WidthRange = &range;
    bound.HeightRange = &range;

    option.BoundsRange = &bound;

    char *tokens[2];
    tokens[0] = (char *)"token0";
    tokens[1] = (char *)"token1";

    option.__sizeVideoSourceTokensAvailable = 2;
    option.VideoSourceTokensAvailable = tokens;

    tt__VideoSourceConfigurationOptionsExtension extension;
    tt__RotateOptions rotate;
    bool val2 = true;
    rotate.Reboot = &val2;

    tt__RotateMode mode[2] = {(tt__RotateMode)1, (tt__RotateMode)0};
    rotate.Mode = mode;
    rotate.__sizeMode = 2;

    int arr[2] = {4, 5};
    tt__IntItems items;
    items.__sizeItems = 2;
    items.Items = arr;
    rotate.DegreeList = &items;

    extension.Rotate = &rotate;

    tt__VideoSourceConfigurationOptionsExtension2 extension2;
    tt__SceneOrientationMode sMode[2] = {(tt__SceneOrientationMode)1, (tt__SceneOrientationMode)0};

    extension2.SceneOrientationMode = sMode;
    extension2.__sizeSceneOrientationMode = 2;

    extension.Extension = &extension2;
    option.Extension = &extension;

    auto err = jsonOperation::parseVideoSourceConfigurationOptions(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "VideoSourceConfigurationOptions: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoSourceMode_V1)
{
    string jsonResponse;
    trt__VideoSourceMode mode;

    mode.token = (char *)"modeToken";

    bool enable = true;
    mode.Enabled = &enable;

    mode.MaxFramerate = 60;

    tt__VideoResolution resolution;
    resolution.Width = 100;
    resolution.Height = 120;
    mode.MaxResolution = &resolution;

    mode.Encodings = (char *)"encodings";
    mode.Reboot = false;
    mode.Description = (char *)"description";

    auto err = jsonOperation::parseVideoSourceMode_V1(jsonResponse, &mode);
    ASSERT_THAT(err, nullopt);

    cout << "videoSourceMode: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoSourceMode_V2)
{
    string jsonResponse;
    ns1__VideoSourceMode mode;

    mode.token = (char *)"modeToken";

    bool enable = true;
    mode.Enabled = &enable;

    mode.MaxFramerate = 60;

    tt__VideoResolution resolution;
    resolution.Width = 100;
    resolution.Height = 120;
    mode.MaxResolution = &resolution;

    mode.Encodings = (char *)"encodings";
    mode.Reboot = false;
    mode.Description = (char *)"description";

    auto err = jsonOperation::parseVideoSourceMode_V2(jsonResponse, &mode);
    ASSERT_THAT(err, nullopt);

    cout << "videoSourceMode: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoEncoderConfiguration_V1)
{
    string jsonResponse;
    tt__VideoEncoderConfiguration config;

    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.GuaranteedFrameRate = nullptr;
    config.Encoding = (tt__VideoEncoding)0;

    tt__VideoResolution resolution;
    resolution.Width = 100;
    resolution.Height = 200;
    config.Resolution = &resolution;

    config.Quality = 0.7;

    tt__VideoRateControl rateControl;
    rateControl.FrameRateLimit = 60;
    rateControl.EncodingInterval = 2;
    rateControl.BitrateLimit = 2;
    config.RateControl = &rateControl;

    tt__Mpeg4Configuration mpeg4;
    mpeg4.GovLength = 12;
    mpeg4.Mpeg4Profile = (tt__Mpeg4Profile)0;
    config.MPEG4 = &mpeg4;

    tt__H264Configuration H264;
    H264.GovLength = 12;
    H264.H264Profile = (tt__H264Profile)0;
    config.H264 = &H264;

    tt__MulticastConfiguration multicast;

    tt__IPAddress addr;
    addr.Type = (tt__IPType)0;
    addr.IPv4Address = (char *)"192.168.1.1";
    addr.IPv6Address = (char *)"IPv6Address";
    multicast.Address = &addr;
    multicast.Port = 2839;
    multicast.TTL = 3290;
    multicast.AutoStart = true;
    config.Multicast = &multicast;

    config.SessionTimeout = (char *)"SessionTimeout";

    auto err = jsonOperation::parseVideoEncoderConfiguration_V1(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "videoEncoderConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoEncoderConfiguration_V2)
{
    string jsonResponse;
    tt__VideoEncoder2Configuration config;

    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.GuaranteedFrameRate = nullptr;
    config.Encoding = (char *)"MPEG4";

    tt__VideoResolution2 resolution;
    resolution.Width = 100;
    resolution.Height = 200;
    config.Resolution = &resolution;

    config.Quality = 0.7;

    bool boolVal = true;
    tt__VideoRateControl2 rateControl;
    rateControl.FrameRateLimit = 60;
    rateControl.ConstantBitRate = &boolVal;
    rateControl.BitrateLimit = 2;
    config.RateControl = &rateControl;

    tt__MulticastConfiguration multicast;

    tt__IPAddress addr;
    addr.Type = (tt__IPType)0;
    addr.IPv4Address = (char *)"192.168.1.1";
    addr.IPv6Address = (char *)"IPv6Address";
    multicast.Address = &addr;
    multicast.Port = 2839;
    multicast.TTL = 3290;
    multicast.AutoStart = true;
    config.Multicast = &multicast;

    auto err = jsonOperation::parseVideoEncoderConfiguration_V2(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "parseVideoEncoderConfiguration_V2: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoEncoderConfigurationOptions_V1_With_Null_Extension)
{
    string jsonResponse;
    tt__VideoEncoderConfigurationOptions option;

    bool GuaranteedFrameRateSupported = false;
    option.GuaranteedFrameRateSupported = &GuaranteedFrameRateSupported;

    tt__IntRange range;
    range.Min = 0;
    range.Max = 100;

    option.QualityRange = &range;

    tt__JpegOptions jpeg;
    tt__VideoResolution *resolution[2];
    resolution[0] = new tt__VideoResolution;
    resolution[0]->Width = 200;
    resolution[0]->Height = 100;
    resolution[1] = new tt__VideoResolution;
    resolution[1]->Width = 680;
    resolution[1]->Height = 510;

    jpeg.ResolutionsAvailable = resolution;
    jpeg.__sizeResolutionsAvailable = 2;
    jpeg.FrameRateRange = &range;
    jpeg.EncodingIntervalRange = &range;

    option.JPEG = &jpeg;

    tt__Mpeg4Options mpeg;
    mpeg.ResolutionsAvailable = resolution;
    mpeg.__sizeResolutionsAvailable = 2;
    mpeg.GovLengthRange = &range;
    mpeg.FrameRateRange = &range;
    mpeg.EncodingIntervalRange = &range;

    tt__Mpeg4Profile Mpeg4ProfilesSupported[2] = {(tt__Mpeg4Profile)0, (tt__Mpeg4Profile)1};
    mpeg.Mpeg4ProfilesSupported = Mpeg4ProfilesSupported;
    mpeg.__sizeMpeg4ProfilesSupported = 2;
    option.MPEG4 = &mpeg;

    tt__H264Options h264;
    h264.ResolutionsAvailable = resolution;
    h264.__sizeResolutionsAvailable = 2;
    h264.GovLengthRange = &range;
    h264.FrameRateRange = &range;
    h264.EncodingIntervalRange = &range;

    tt__H264Profile H264ProfilesSupported[2] = {(tt__H264Profile)0, (tt__H264Profile)1};
    h264.H264ProfilesSupported = H264ProfilesSupported;
    h264.__sizeH264ProfilesSupported = 2;
    option.H264 = &h264;

    option.Extension = nullptr;

    auto err = jsonOperation::parseVideoEncoderConfigurationOptions_V1(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "videoEncoderConfigurationOptions_V1: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoEncoderConfigurationOptions_V1_With_Vaild_Extension)
{
    string jsonResponse;
    tt__VideoEncoderConfigurationOptions option;

    bool GuaranteedFrameRateSupported = false;
    option.GuaranteedFrameRateSupported = &GuaranteedFrameRateSupported;

    tt__IntRange range;
    range.Min = 0;
    range.Max = 100;

    option.QualityRange = &range;

    // extension与原本的同时存在
    tt__JpegOptions jpeg;
    tt__VideoResolution *resolution[2];
    resolution[0] = new tt__VideoResolution;
    resolution[0]->Width = 200;
    resolution[0]->Height = 100;
    resolution[1] = new tt__VideoResolution;
    resolution[1]->Width = 680;
    resolution[1]->Height = 510;

    jpeg.ResolutionsAvailable = resolution;
    jpeg.__sizeResolutionsAvailable = 2;
    jpeg.FrameRateRange = &range;
    jpeg.EncodingIntervalRange = &range;

    option.JPEG = &jpeg;

    tt__Mpeg4Options mpeg;
    mpeg.ResolutionsAvailable = resolution;
    mpeg.__sizeResolutionsAvailable = 2;
    mpeg.GovLengthRange = &range;
    mpeg.FrameRateRange = &range;
    mpeg.EncodingIntervalRange = &range;

    tt__Mpeg4Profile Mpeg4ProfilesSupported[2] = {(tt__Mpeg4Profile)0, (tt__Mpeg4Profile)1};
    mpeg.Mpeg4ProfilesSupported = Mpeg4ProfilesSupported;
    mpeg.__sizeMpeg4ProfilesSupported = 2;
    option.MPEG4 = &mpeg;

    tt__H264Options h264;
    h264.ResolutionsAvailable = resolution;
    h264.__sizeResolutionsAvailable = 2;
    h264.GovLengthRange = &range;
    h264.FrameRateRange = &range;
    h264.EncodingIntervalRange = &range;

    tt__H264Profile H264ProfilesSupported[2] = {(tt__H264Profile)0, (tt__H264Profile)1};
    h264.H264ProfilesSupported = H264ProfilesSupported;
    h264.__sizeH264ProfilesSupported = 2;
    option.H264 = &h264;

    // extension下的参数
    tt__VideoEncoderOptionsExtension extension;

    tt__IntRange range2;
    range2.Min = 346;
    range2.Max = 2362;

    tt__JpegOptions2 jpeg_v2;
    tt__VideoResolution *resolution2[2];
    resolution2[0] = new tt__VideoResolution;
    resolution2[0]->Width = 1000;
    resolution2[0]->Height = 2000;
    resolution2[1] = new tt__VideoResolution;
    resolution2[1]->Width = 149;
    resolution2[1]->Height = 560;

    jpeg_v2.ResolutionsAvailable = resolution2;
    jpeg_v2.__sizeResolutionsAvailable = 2;
    jpeg_v2.FrameRateRange = &range2;
    jpeg_v2.EncodingIntervalRange = &range2;
    jpeg_v2.BitrateRange = &range2;

    extension.JPEG = &jpeg_v2;

    tt__Mpeg4Options2 mpeg_v2;
    mpeg_v2.ResolutionsAvailable = resolution2;
    mpeg_v2.__sizeResolutionsAvailable = 2;
    mpeg_v2.GovLengthRange = &range2;
    mpeg_v2.FrameRateRange = &range2;
    mpeg_v2.EncodingIntervalRange = &range2;

    tt__Mpeg4Profile Mpeg4ProfilesSupported2[2] = {(tt__Mpeg4Profile)0, (tt__Mpeg4Profile)1};
    mpeg_v2.Mpeg4ProfilesSupported = Mpeg4ProfilesSupported2;
    mpeg_v2.__sizeMpeg4ProfilesSupported = 2;
    mpeg_v2.BitrateRange = &range2;
    extension.MPEG4 = &mpeg_v2;

    tt__H264Options2 h264_v2;
    h264_v2.ResolutionsAvailable = resolution2;
    h264_v2.__sizeResolutionsAvailable = 2;
    h264_v2.GovLengthRange = &range2;
    h264_v2.FrameRateRange = &range2;
    h264_v2.EncodingIntervalRange = &range2;

    tt__H264Profile H264ProfilesSupported2[2] = {(tt__H264Profile)0, (tt__H264Profile)1};
    h264_v2.H264ProfilesSupported = H264ProfilesSupported2;
    h264_v2.__sizeH264ProfilesSupported = 2;
    h264_v2.BitrateRange = &range2;
    extension.H264 = &h264_v2;

    option.Extension = &extension;

    auto err = jsonOperation::parseVideoEncoderConfigurationOptions_V1(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "videoEncoderConfigurationOptions_V1: " << jsonResponse << endl;

    json j = json::parse(jsonResponse);

    ASSERT_TRUE(j["JPEG"]["resolutionsAvailable"][0]["width"].get<string>() == string("1000"));
    ASSERT_TRUE(j["MPEG4"]["govLengthRange"].get<string>() == string("346 ~ 2362"));
}

TEST_F(jsonOperationTest, parseVideoEncoderConfigurationOptions_V2)
{
    string jsonResponse;
    tt__VideoEncoder2ConfigurationOptions option;

    bool boolVal = true;

    option.GovLengthRange = (char *)"GovLengthRange";
    option.FrameRatesSupported = (char *)"GovLengthRange";
    option.ProfilesSupported = (char *)"GovLengthRange";
    option.ConstantBitRateSupported = &boolVal;
    option.GuaranteedFrameRateSupported = &boolVal;
    option.Encoding = (char *)"GovLengthRange";

    tt__IntRange range;
    range.Min = 0;
    range.Max = 1;

    option.QualityRange = nullptr;
    option.ResolutionsAvailable = nullptr;
    option.BitrateRange = &range;

    auto err = jsonOperation::parseVideoEncoderConfigurationOptions_V2(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "parseVideoEncoderConfigurationOptions_V2: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioSourceConfiguration)
{
    string jsonResponse;
    tt__AudioSourceConfiguration config;
    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.SourceToken = (char *)"sourceToken";

    auto err = jsonOperation::parseAudioSourceConfiguration(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "AudioOutputConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioOutputConfiguration)
{
    string jsonResponse;
    tt__AudioOutputConfiguration config;
    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.OutputToken = (char *)"outputToken";
    config.SendPrimacy = (char *)"sendPrimacy";
    config.OutputLevel = 1;

    auto err = jsonOperation::parseAudioOutputConfiguration(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "AudioOutputConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioOutputConfigurationOptions)
{
    string jsonResponse;
    tt__AudioOutputConfigurationOptions option;

    char *tokens[2];
    tokens[0] = (char *)"token0";
    tokens[1] = (char *)"token1";
    option.OutputTokensAvailable = tokens;
    option.__sizeOutputTokensAvailable = 2;

    char *sendPrimacyOptions[2];
    sendPrimacyOptions[0] = (char *)"sendPrimacyOptions0";
    sendPrimacyOptions[1] = (char *)"sendPrimacyOptions1";
    option.SendPrimacyOptions = sendPrimacyOptions;
    option.__sizeSendPrimacyOptions = 2;

    tt__IntRange range;
    range.Max = 2;
    range.Min = 1;
    option.OutputLevelRange = &range;

    auto err = jsonOperation::parseAudioOutputConfigurationOptions(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "AudioOutputConfigurationOptions: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioEncoderConfiguration_V1)
{
    string jsonResponse;
    tt__AudioEncoderConfiguration config;

    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.Encoding = (tt__AudioEncoding)1;
    config.Bitrate = 23;
    config.SampleRate = 21;

    tt__MulticastConfiguration multicast;

    tt__IPAddress addr;
    addr.Type = (tt__IPType)0;
    addr.IPv4Address = (char *)"192.168.1.1";
    addr.IPv6Address = (char *)"ipv6Address";
    multicast.Address = &addr;
    multicast.Port = 899;
    multicast.TTL = 32;
    multicast.AutoStart = true;
    config.Multicast = &multicast;

    config.SessionTimeout = (char *)"sessionTimeOut";

    auto err = jsonOperation::parseAudioEncoderConfiguration_V1(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "AudioEncoderConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioEncoderConfiguration_V2)
{
    string jsonResponse;
    tt__AudioEncoder2Configuration config;

    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.Encoding = (char *)"G722";
    config.Bitrate = 23;
    config.SampleRate = 21;

    tt__MulticastConfiguration multicast;

    tt__IPAddress addr;
    addr.Type = (tt__IPType)0;
    addr.IPv4Address = (char *)"192.168.1.1";
    addr.IPv6Address = (char *)"ipv6Address";
    multicast.Address = &addr;
    multicast.Port = 899;
    multicast.TTL = 32;
    multicast.AutoStart = true;
    config.Multicast = &multicast;

    auto err = jsonOperation::parseAudioEncoderConfiguration_V2(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "AudioEncoderConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioEncoderConfigurationOptions)
{
    string jsonResponse;
    tt__AudioEncoderConfigurationOptions option;

    tt__AudioEncoderConfigurationOption *options[2];
    options[0] = new tt__AudioEncoderConfigurationOption;
    options[1] = new tt__AudioEncoderConfigurationOption;

    int list[2] = {1090, 1092};
    options[0]->Encoding = (tt__AudioEncoding)0;

    tt__IntItems intItem;
    intItem.__sizeItems = 2;
    intItem.Items = list;

    options[0]->BitrateList = &intItem;
    options[0]->SampleRateList = &intItem;

    int list2[2] = {1678, 198};
    options[1]->Encoding = (tt__AudioEncoding)1;

    tt__IntItems intItem2;
    intItem2.__sizeItems = 2;
    intItem2.Items = list2;
    options[1]->BitrateList = &intItem2;
    options[1]->SampleRateList = &intItem2;

    option.__sizeOptions = 2;
    option.Options = options;

    auto err = jsonOperation::parseAudioEncoderConfigurationOptions(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "AudioEncoderConfigurationOptions: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioDecoderConfiguration)
{
    string jsonResponse;
    tt__AudioDecoderConfiguration config;
    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;

    auto err = jsonOperation::parseAudioDecoderConfiguration(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "AudioDecoderConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioDecoderConfigurationOptions_V1)
{
    string jsonResponse;
    tt__AudioDecoderConfigurationOptions option;

    int list[2] = {1090, 1092};

    tt__IntItems intItem;
    intItem.__sizeItems = 2;
    intItem.Items = list;

    tt__AACDecOptions aac;
    aac.Bitrate = &intItem;
    aac.SampleRateRange = &intItem;
    option.AACDecOptions = &aac;

    tt__G711DecOptions g711;
    g711.Bitrate = &intItem;
    g711.SampleRateRange = &intItem;
    option.G711DecOptions = &g711;

    tt__G726DecOptions g726;
    g726.Bitrate = &intItem;
    g726.SampleRateRange = &intItem;
    option.G726DecOptions = &g726;

    auto err = jsonOperation::parseAudioDecoderConfigurationOptions_V1(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "AudioDecoderConfigurationOptions: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseAudioEncoderDecoderConfigurationOptions_V2)
{
    string jsonResponse;
    tt__AudioEncoder2ConfigurationOptions option;

    int list[2] = {1090, 1092};

    tt__IntItems intItem;
    intItem.__sizeItems = 2;
    intItem.Items = list;

    option.Encoding = (char *)"G711";
    option.BitrateList = &intItem;
    option.SampleRateList = &intItem;

    auto err = jsonOperation::parseAudioEncoderDecoderConfigurationOptions_V2(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "AudioDecoderConfigurationOptions: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseMetadataConfiguration)
{
    string jsonResponse;
    tt__MetadataConfiguration config;

    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.CompressionType = (char *)"compressionType";

    bool boolVal = true;
    config.GeoLocation = &boolVal;
    config.ShapePolygon = &boolVal;

    tt__PTZFilter ptzStatus;
    ptzStatus.Status = true;
    ptzStatus.Position = true;
    config.PTZStatus = &ptzStatus;

    tt__EventSubscription event;
    wsnt__FilterType filter;
    event.Filter = &filter;
    _tt__EventSubscription_SubscriptionPolicy subscription;
    event.SubscriptionPolicy = &subscription;
    config.Events = &event;

    config.Analytics = &boolVal;

    tt__MulticastConfiguration multicast;

    tt__IPAddress addr;
    addr.Type = (tt__IPType)0;
    addr.IPv4Address = (char *)"192.168.1.1";
    addr.IPv6Address = (char *)"IPv6Address";
    multicast.Address = &addr;
    multicast.Port = 2839;
    multicast.TTL = 3290;
    multicast.AutoStart = true;
    config.Multicast = &multicast;

    config.SessionTimeout = (char *)"SessionTimeout";

    tt__AnalyticsEngineConfiguration analyticsEngineConfiguration;

    tt__Config *analyticsModule[1];
    analyticsModule[0] = new tt__Config;

    analyticsModule[0]->Name = (char *)"name";
    analyticsModule[0]->Type = (char *)"type";

    tt__ItemList parameters;

    _tt__ItemList_SimpleItem simpleItem[2];
    simpleItem[0].Name = (char *)"name";
    simpleItem[0].Value = (char *)"value";
    simpleItem[1].Name = (char *)"name2";
    simpleItem[1].Value = (char *)"value2";

    parameters.SimpleItem = simpleItem;
    parameters.__sizeSimpleItem = 2;

    parameters.ElementItem = nullptr;
    parameters.__sizeElementItem = 0;
    parameters.Extension = nullptr;

    analyticsModule[0]->Parameters = &parameters;
    analyticsEngineConfiguration.AnalyticsModule = analyticsModule;
    analyticsEngineConfiguration.__sizeAnalyticsModule = 1;
    analyticsEngineConfiguration.Extension = nullptr;

    config.AnalyticsEngineConfiguration = &analyticsEngineConfiguration;
    config.Extension = nullptr;

    auto err = jsonOperation::parseMetadataConfiguration(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "MetadataConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseMetadataConfigurationOptions)
{
    string jsonResponse;
    tt__MetadataConfigurationOptions option;

    bool boolVal = true;
    int intVal = 4;
    option.GeoLocation = &boolVal;
    option.MaxContentFilterSize = &intVal;

    tt__PTZStatusFilterOptions ptzStatus;
    ptzStatus.PanTiltStatusSupported = true;
    ptzStatus.ZoomStatusSupported = false;
    ptzStatus.PanTiltPositionSupported = &boolVal;
    ptzStatus.ZoomPositionSupported = &boolVal;

    option.PTZStatusFilterOptions = &ptzStatus;

    tt__MetadataConfigurationOptionsExtension extension;
    char *strList[2];
    strList[0] = (char *)"CompressionType1";
    strList[1] = (char *)"CompressionType2";
    extension.__sizeCompressionType = 2;
    extension.CompressionType = strList;

    option.Extension = &extension;

    auto err = jsonOperation::parseMetadataConfigurationOptions(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "parseMetadataConfigurationOptions: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseVideoAnalyticsConfiguration)
{
    string jsonResponse;
    tt__VideoAnalyticsConfiguration config;

    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;

    tt__AnalyticsEngineConfiguration analyticsEngineConfiguration;

    tt__Config *tt_config_[2];
    tt_config_[0] = new tt__Config;
    tt_config_[1] = new tt__Config;

    tt_config_[0]->Name = (char *)"name0";
    tt_config_[0]->Type = (char *)"type0";
    tt_config_[1]->Name = (char *)"name1";
    tt_config_[1]->Type = (char *)"type1";

    tt__ItemList parameters;

    _tt__ItemList_SimpleItem simpleItem[2];
    simpleItem[0].Name = (char *)"name";
    simpleItem[0].Value = (char *)"value";
    simpleItem[1].Name = (char *)"name2";
    simpleItem[1].Value = (char *)"value2";

    parameters.SimpleItem = simpleItem;
    parameters.__sizeSimpleItem = 2;

    parameters.ElementItem = nullptr;
    parameters.__sizeElementItem = 0;
    parameters.Extension = nullptr;

    tt_config_[0]->Parameters = &parameters;
    tt_config_[1]->Parameters = &parameters;
    analyticsEngineConfiguration.AnalyticsModule = tt_config_;
    analyticsEngineConfiguration.__sizeAnalyticsModule = 2;
    analyticsEngineConfiguration.Extension = nullptr;

    config.AnalyticsEngineConfiguration = &analyticsEngineConfiguration;

    tt__RuleEngineConfiguration ruleEngineConfiguration;
    ruleEngineConfiguration.Rule = tt_config_;
    ruleEngineConfiguration.__sizeRule = 2;
    config.RuleEngineConfiguration = &ruleEngineConfiguration;

    auto err = jsonOperation::parseVideoAnalyticsConfiguration(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "VideoAnalyticsConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parsePTZConfiguration)
{
    string jsonResponse;
    tt__PTZConfiguration config;
    int intVal = 2;

    config.token = (char *)"token";
    config.Name = (char *)"name";
    config.UseCount = 2;
    config.MoveRamp = &intVal;
    config.PresetRamp = &intVal;
    config.PresetTourRamp = &intVal;
    config.NodeToken = (char *)"NodeToken";
    config.DefaultAbsolutePantTiltPositionSpace = (char *)"DefaultAbsolutePantTiltPositionSpace";
    config.DefaultAbsoluteZoomPositionSpace = (char *)"DefaultAbsoluteZoomPositionSpace";
    config.DefaultRelativePanTiltTranslationSpace = (char *)"DefaultRelativePanTiltTranslationSpace";
    config.DefaultRelativeZoomTranslationSpace = (char *)"DefaultRelativeZoomTranslationSpace";
    config.DefaultContinuousPanTiltVelocitySpace = (char *)"DefaultContinuousPanTiltVelocitySpace";
    config.DefaultContinuousZoomVelocitySpace = (char *)"DefaultContinuousZoomVelocitySpace";

    tt__PTZSpeed PTZSpeed;
    tt__Vector2D v2d;
    v2d.x = 121;
    v2d.y = 1234;
    v2d.space = (char *)"space";
    PTZSpeed.PanTilt = &v2d;

    tt__Vector1D v1d;
    v1d.x = 989;
    v1d.space = (char *)"space";
    PTZSpeed.Zoom = &v1d;
    config.DefaultPTZSpeed = &PTZSpeed;
    config.DefaultPTZTimeout = (char *)"DefaultPTZTimeout";

    tt__PanTiltLimits PanTiltLimits;
    tt__Space2DDescription space2D;
    space2D.URI = (char *)"uri";
    tt__FloatRange floatRange;
    floatRange.Min = 84;
    floatRange.Max = 145;
    space2D.XRange = &floatRange;
    space2D.YRange = &floatRange;
    PanTiltLimits.Range = &space2D;
    config.PanTiltLimits = &PanTiltLimits;
    config.ZoomLimits = nullptr;
    tt__PTZConfigurationExtension extension;

    tt__PTControlDirection PTControlDirection;
    PTControlDirection.EFlip = nullptr;

    tt__Reverse reverse;
    reverse.Mode = tt__ReverseMode__AUTO;
    PTControlDirection.Reverse = &reverse;
    extension.PTControlDirection = &PTControlDirection;
    config.Extension = &extension;

    auto err = jsonOperation::parsePTZConfiguration(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "parsePTZConfiguration: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parsePTZConfigurationOptions)
{
    string jsonResponse;
    tt__PTZConfigurationOptions option;

    option.PTZRamps = (char *)"PTZRamps";

    tt__PTZSpaces space;
    space.__sizeAbsolutePanTiltPositionSpace = 2;
    space.AbsolutePanTiltPositionSpace = new tt__Space2DDescription *[2];
    space.AbsolutePanTiltPositionSpace[0] = new tt__Space2DDescription();
    space.AbsolutePanTiltPositionSpace[1] = new tt__Space2DDescription();
    space.AbsolutePanTiltPositionSpace[0]->URI = (char *)"uriuriuriuri";
    space.AbsolutePanTiltPositionSpace[0]->XRange = new tt__FloatRange();
    space.AbsolutePanTiltPositionSpace[0]->XRange->Min = 0.00;
    space.AbsolutePanTiltPositionSpace[0]->XRange->Max = 1.25;
    space.AbsolutePanTiltPositionSpace[0]->YRange = new tt__FloatRange();
    space.AbsolutePanTiltPositionSpace[0]->YRange->Min = 0.00;
    space.AbsolutePanTiltPositionSpace[0]->YRange->Max = 1.25;

    space.AbsolutePanTiltPositionSpace[1]->URI = (char *)"uri2uri2uri2uri2";
    space.AbsolutePanTiltPositionSpace[1]->XRange = new tt__FloatRange();
    space.AbsolutePanTiltPositionSpace[1]->XRange->Min = 2.00;
    space.AbsolutePanTiltPositionSpace[1]->XRange->Max = 2.25;
    space.AbsolutePanTiltPositionSpace[1]->YRange = new tt__FloatRange();
    space.AbsolutePanTiltPositionSpace[1]->YRange->Min = 4.00;
    space.AbsolutePanTiltPositionSpace[1]->YRange->Max = 4.25;
    option.Spaces = &space;

    tt__DurationRange PTZTimeout;
    PTZTimeout.Max = (char *)"max";
    PTZTimeout.Min = (char *)"min";
    option.PTZTimeout = &PTZTimeout;

    tt__PTControlDirectionOptions PTControlDirection;

    tt__EFlipOptions EFlip;
    tt__EFlipMode modes[2] = {tt__EFlipMode__OFF, tt__EFlipMode__ON};
    EFlip.__sizeMode = 2;
    EFlip.Mode = modes;

    PTControlDirection.EFlip = &EFlip;
    PTControlDirection.Reverse = nullptr;
    option.PTControlDirection = &PTControlDirection;

    auto err = jsonOperation::parsePTZConfigurationOptions(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "parsePTZConfigurationOptions: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseOSD)
{
    string jsonResponse;
    tt__OSDConfiguration config;

    config.token = (char *)"token";

    tt__OSDReference configToken;
    configToken.__item = (char *)"VideoSourceConfigurationToken";
    config.VideoSourceConfigurationToken = &configToken;

    config.Type = (tt__OSDType)0;
    tt__OSDPosConfiguration position;
    position.Type = (char *)"upperLeft";

    float val1 = 1.1;
    tt__Vector pos;
    pos.x = &val1;
    pos.y = &val1;
    position.Pos = &pos;
    position.Extension = nullptr;

    config.Position = &position;

    tt__OSDTextConfiguration textString;

    bool isPersistentText = true;
    textString.IsPersistentText = &isPersistentText;

    textString.Type = (char *)"Plain";
    textString.DateFormat = (char *)"yyyy/MM/dd";
    textString.TimeFormat = (char *)"HH:mm:ss";

    int val2 = 100;

    textString.FontSize = &val2;

    tt__OSDColor osdColor;
    osdColor.Transparent = &val2;
    tt__Color color;
    color.X = 255;
    color.Y = 255;
    color.Z = 255;
    color.Colorspace = (char *)"Colorspace";
    color.Likelihood = &val1;
    osdColor.Color = &color;
    textString.FontColor = &osdColor;
    textString.BackgroundColor = &osdColor;
    textString.PlainText = (char *)"PlainText";
    textString.Extension = nullptr;

    config.TextString = &textString;

    tt__OSDImgConfiguration Image;
    Image.ImgPath = (char *)"ImgPath";
    Image.Extension = nullptr;
    config.Image = &Image;
    config.Extension = nullptr;

    auto err = jsonOperation::parseOSD(jsonResponse, &config);
    ASSERT_THAT(err, nullopt);

    cout << "OSD: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseOSDOptions)
{
    string jsonResponse;
    tt__OSDConfigurationOptions option;

    tt__MaximumNumberOfOSDs MaximumNumberOfOSDs;
    MaximumNumberOfOSDs.Total = 2;
    MaximumNumberOfOSDs.Image = nullptr;
    MaximumNumberOfOSDs.PlainText = nullptr;
    MaximumNumberOfOSDs.Date = nullptr;
    MaximumNumberOfOSDs.Time = nullptr;
    MaximumNumberOfOSDs.DateAndTime = nullptr;

    option.MaximumNumberOfOSDs = &MaximumNumberOfOSDs;

    tt__OSDType type[2] = {tt__OSDType__Image, tt__OSDType__Text};
    option.Type = type;
    option.__sizeType = 2;

    option.PositionOption = nullptr;
    option.__sizePositionOption = 0;

    tt__OSDTextOptions textOption;
    textOption.Type = nullptr;
    textOption.__sizeType = 0;
    textOption.FontSizeRange = nullptr;
    textOption.DateFormat = nullptr;
    textOption.TimeFormat = nullptr;

    tt__OSDColorOptions ColorOptions;
    tt__ColorOptions color;

    tt__Color *colorList[2];
    colorList[0] = new tt__Color;
    colorList[1] = new tt__Color;
    colorList[0]->X = 1.0;
    colorList[0]->Y = 2.0;
    colorList[0]->Z = 3.0;
    colorList[0]->Likelihood = nullptr;
    colorList[0]->Colorspace = (char *)"colorSpace";

    colorList[1]->X = 1.0;
    colorList[1]->Y = 2.0;
    colorList[1]->Z = 3.0;
    colorList[1]->Likelihood = nullptr;
    colorList[1]->Colorspace = (char *)"colorSpace";

    color.ColorList = colorList;
    color.__sizeColorList = 2;
    color.ColorspaceRange = nullptr;
    color.__sizeColorspaceRange = 0;

    ColorOptions.Color = &color;

    tt__IntRange transparent;
    transparent.Min = 0;
    transparent.Max = 1;
    ColorOptions.Transparent = &transparent;

    textOption.FontColor = &ColorOptions;
    textOption.BackgroundColor = nullptr;

    option.TextOption = &textOption;
    option.ImageOption = nullptr;

    auto err = jsonOperation::parseOSDOptions(jsonResponse, &option);
    ASSERT_THAT(err, nullopt);

    cout << "parseOSDOptions: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, parseRecordingInformation)
{
    string jsonResponse;

    tt__RecordingInformation info;

    info.RecordingToken = (char *)"recordingToken";

    tt__RecordingSourceInformation Source;
    Source.SourceId = (char *)"sourceId";
    Source.Name = (char *)"name";
    Source.Location = (char *)"location";
    Source.Description = (char *)"description";
    Source.Address = (char *)"address";
    info.Source = &Source;
    time_t time_;
    time(&time_);
    info.EarliestRecording = &time_;
    info.LatestRecording = &time_;
    info.Content = (char *)"content";

    tt__TrackInformation *track[2];
    tt__TrackInformation trackItem;
    track[0] = &trackItem;
    track[1] = &trackItem;

    trackItem.TrackToken = (char *)"TrackToken";
    trackItem.TrackType = tt__TrackType__Video;
    trackItem.Description = (char *)"Description";
    trackItem.DataFrom = time_;
    trackItem.DataTo = time_;

    info.Track = track;
    info.__sizeTrack = 2;

    info.RecordingStatus = tt__RecordingStatus__Removed;

    auto err = jsonOperation::parseRecordingInformation(jsonResponse, &info);
    ASSERT_THAT(err, nullopt);

    cout << "parseRecordingInformation: " << jsonResponse << endl;
}

TEST_F(jsonOperationTest, buildNetworkInterfaceSetConfiguration)
{
    string jsonResponse;
    tt__NetworkInterfaceSetConfiguration networkInterface;
    ONVIF_SET_NETWORK_INTERFACE_PARAM param;
    ONVIF_NETWORK_LINK_CONFIGURATION link;
    link.autoNegotiation = true;
    link.duplex = 0;
    link.speed = 100;

    char **IPv4Address;
    IPv4Address = new char *[2];
    IPv4Address[0] = (char *)"192.168.1.1";
    IPv4Address[1] = (char *)"192.168.1.2";

    int IPv4PrefixLen[] = {24, 25};

    char **IPv6Address;
    IPv6Address = new char *[1];
    IPv6Address[0] = (char *)"::1";

    int IPv6PrefixLen[] = {128};

    param.interfaceToken = (char *)"interfaceToken";
    param.enable = true;
    param.MTU = 300;
    param.link = &link;

    param.IPv4Enabled = true;
    param.IPv4DHCP = false;
    param.sizeIPv4 = 2;
    param.IPv4Address = IPv4Address;
    param.IPv4PrefixLen = IPv4PrefixLen;

    param.IPv6Enabled = true;
    param.IPv6DHCP = 1;
    param.sizeIPv6 = 1;
    param.IPv6Address = IPv6Address;
    param.IPv6PrefixLen = IPv6PrefixLen;
    param.acceptRouterAdvert = true;

    ONVIF_DOT11_CONFIGURATION *extension;
    extension = new ONVIF_DOT11_CONFIGURATION[1];
    extension[0].SSID = (char *)"WIFI_NAME";
    extension[0].sizeSSID = sizeof("WIFI_NAME");
    extension[0].alias = (char *)"WIFI_NAME";
    extension[0].mode = 0;
    extension[0].priority = nullptr;
    extension[0].securityMode = 2;
    extension[0].securityAlgorithm = 0;
    extension[0].PSKKey = (char *)"key";
    extension[0].PSKPassphrase = (char *)"passphrase";
    extension[0].dot1XConfigurationToken = (char *)"token";
    param.sizeExtension = 1;
    param.extension = &extension;

    auto err = jsonOperation::buildNetworkInterfaceSetConfiguration(networkInterface, &param);
    ASSERT_THAT(err, nullopt);
    ASSERT_TRUE(*networkInterface.Enabled == param.enable);
    ASSERT_TRUE(*networkInterface.MTU == param.MTU);

    ASSERT_TRUE(networkInterface.Link->AutoNegotiation == param.link->autoNegotiation);
    ASSERT_TRUE(networkInterface.Link->Speed == param.link->speed);
    ASSERT_TRUE(networkInterface.Link->Duplex == (tt__Duplex)param.link->duplex);

    ASSERT_TRUE(*networkInterface.IPv4->Enabled == param.IPv4Enabled);
    ASSERT_TRUE(networkInterface.IPv4->__sizeManual == param.sizeIPv4);
    ASSERT_TRUE(*networkInterface.IPv4->DHCP == param.IPv4DHCP);
    ASSERT_TRUE(strcmp(networkInterface.IPv4->Manual[0]->Address, param.IPv4Address[0]) == 0);
    ASSERT_TRUE(networkInterface.IPv4->Manual[0]->PrefixLength == param.IPv4PrefixLen[0]);
    ASSERT_TRUE(strcmp(networkInterface.IPv4->Manual[1]->Address, param.IPv4Address[1]) == 0);
    ASSERT_TRUE(networkInterface.IPv4->Manual[1]->PrefixLength == param.IPv4PrefixLen[1]);

    ASSERT_TRUE(*networkInterface.IPv6->Enabled == param.IPv6Enabled);
    ASSERT_TRUE(networkInterface.IPv6->__sizeManual == param.sizeIPv6);
    ASSERT_TRUE(*networkInterface.IPv6->AcceptRouterAdvert == param.acceptRouterAdvert);
    ASSERT_TRUE(*networkInterface.IPv6->DHCP == (tt__IPv6DHCPConfiguration)param.IPv6DHCP);
    ASSERT_TRUE(strcmp(networkInterface.IPv6->Manual[0]->Address, param.IPv6Address[0]) == 0);
    ASSERT_TRUE(networkInterface.IPv6->Manual[0]->PrefixLength == param.IPv6PrefixLen[0]);

    ASSERT_TRUE(networkInterface.Extension->__sizeDot3 == 0);
    ASSERT_TRUE(networkInterface.Extension->Dot3 == nullptr);
    ASSERT_TRUE(networkInterface.Extension->__sizeDot11 == 1);
    ASSERT_TRUE(strcmp((char *)networkInterface.Extension->Dot11[0]->SSID.__ptr, param.extension[0]->SSID) == 0);
    ASSERT_TRUE(networkInterface.Extension->Dot11[0]->SSID.__size == param.extension[0]->sizeSSID);
    ASSERT_TRUE(strcmp(networkInterface.Extension->Dot11[0]->Alias, param.extension[0]->alias) == 0);
    ASSERT_TRUE(networkInterface.Extension->Dot11[0]->Mode == (tt__Dot11StationMode)param.extension[0]->mode);
    ASSERT_TRUE(networkInterface.Extension->Dot11[0]->Priority == nullptr);
    ASSERT_TRUE(networkInterface.Extension->Dot11[0]->Security->Mode == (tt__Dot11SecurityMode)param.extension[0]->securityMode);
    ASSERT_TRUE(*networkInterface.Extension->Dot11[0]->Security->Algorithm == (tt__Dot11Cipher)param.extension[0]->securityAlgorithm);
    ASSERT_TRUE(strcmp((char *)networkInterface.Extension->Dot11[0]->Security->PSK->Key->__ptr, param.extension[0]->PSKKey) == 0);
    ASSERT_TRUE(strcmp(networkInterface.Extension->Dot11[0]->Security->PSK->Passphrase, param.extension[0]->PSKPassphrase) == 0);
    ASSERT_TRUE(strcmp(networkInterface.Extension->Dot11[0]->Security->Dot1X, param.extension[0]->dot1XConfigurationToken) == 0);
    jsonOperation::deleteNetworkInterfaceSetConfiguration(&networkInterface);
}

TEST_F(jsonOperationTest, buildOSDConfiguration)
{
    string jsonResponse;
    tt__OSDConfiguration OSD;
    ONVIF_SET_OSD_PARAM param = {0};

    param.OSDConfigurationToken = (char *)"OSDConfigurationToken";
    param.videoSourceConfigurationToken = (char *)"videoSourceConfigurationToken";
    param.type = 0;
    param.positionType = 4;
    param.positionX = nullptr;
    param.positionY = nullptr;
    param.plainText = (char *)"palinText";
    param.isPersistentText = nullptr;
    param.textType = 0;
    param.fontSize = nullptr;
    param.fontTransparent = nullptr;
    param.fontColor = nullptr;
    param.backgroundColor = nullptr;
    param.backgroundTransparent = nullptr;
    param.dateFormat = 0;
    param.timeFormat = 0;
    param.imgPath = (char *)"imgPath";

    auto err = jsonOperation::buildOSDConfiguration(OSD, &param);
    ASSERT_THAT(err, nullopt);
    ASSERT_TRUE(OSD.VideoSourceConfigurationToken->__item != nullptr);
    ASSERT_TRUE(strcmp(OSD.VideoSourceConfigurationToken->__item, param.videoSourceConfigurationToken) == 0);
    ASSERT_TRUE(OSD.Type == tt__OSDType__Text);
    ASSERT_TRUE(strcmp(OSD.Position->Type, "Custom") == 0);
    ASSERT_TRUE(OSD.Position->Pos == nullptr);
    ASSERT_TRUE(OSD.TextString != nullptr);
    ASSERT_TRUE(strcmp(OSD.TextString->PlainText, param.plainText) == 0);
    ASSERT_TRUE(OSD.TextString->IsPersistentText == nullptr);
    ASSERT_TRUE(strcmp(OSD.TextString->Type, "Plain") == 0);
    ASSERT_TRUE(OSD.TextString->FontColor == nullptr);
    ASSERT_TRUE(OSD.TextString->BackgroundColor == nullptr);
    ASSERT_TRUE(OSD.TextString->FontSize == nullptr);
    ASSERT_TRUE(OSD.TextString->DateFormat == nullptr);
    ASSERT_TRUE(OSD.TextString->TimeFormat == nullptr);
    ASSERT_TRUE(OSD.Image == nullptr);

    jsonOperation::deleteOSDConfiguration(&OSD);
}
